package com.zang.blogz.aspect;

import cn.dev33.satoken.stp.StpUtil;
import com.alibaba.fastjson.JSON;
import com.zang.blogz.constant.ResultConstant;
import com.zang.blogz.entity.ExceptionLog;
import com.zang.blogz.event.ExceptionLogEvent;
import io.github.vampireachao.stream.core.optional.Opp;
import com.zang.blogz.utils.ErrorUtils;
import com.zang.blogz.utils.IpUtil;
import io.swagger.annotations.ApiOperation;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Objects;

/**
 * @Author: ZVerify
 * @Description: 异常处理切面
 * @DateTime: 2022/9/3 14:29
 **/
@Aspect
@Component
public class ExceptionLogAspect {

    @Autowired
    private ApplicationContext applicationContext;

    @Pointcut("execution(* com.zang.blogz.controller..*.*(..))")
    public void exceptionLogPointcut() {

    }

    @AfterThrowing(value = "exceptionLogPointcut()", throwing = "e")
    public void saveExceptionLog(JoinPoint joinPoint, Exception e) {

        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = (HttpServletRequest) Objects.requireNonNull(requestAttributes).resolveReference(RequestAttributes.REFERENCE_REQUEST);

        ExceptionLog exceptionLog = new ExceptionLog();
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        ApiOperation apiOperation = method.getAnnotation(ApiOperation.class);

        // 异常信息
        exceptionLog.setResponseData(ErrorUtils.getTrace(e));

        // ip地址
        String ipAddress = IpUtil.getIpAddress(request);
        exceptionLog.setIpAddress(ipAddress);

        // ip来源
        exceptionLog.setIpSource(IpUtil.getIpSource(ipAddress));

        // 请求用户ID
        Opp<Integer> id = Opp.ofTry(() -> (Integer) StpUtil.getExtra("id"));

        Opp.of(id.getException())

                .ifPresent(i -> exceptionLog.setUserId(-1))
                .orElseRun(() -> exceptionLog.setUserId(id.get()));

        // 请求用户昵称
        Opp<String> nameOpp = Opp.ofTry(() -> (String) StpUtil.getExtra("name"));

        Opp.of(nameOpp.getException())

                .ifPresent((k) -> exceptionLog.setNickname("未登录用户"))
                .orElseRun(() -> exceptionLog.setNickname(nameOpp.get()));

        // 请求url
        exceptionLog.setOptUrl(Objects.requireNonNull(request).getRequestURI());
        String className = joinPoint.getTarget().getClass().getName();
        String methodName = method.getName();
        methodName = className + "." + methodName;

        // 请求方法
        Opp.of(methodName).peek(exceptionLog::setOptMethod);

        // 请求方式
        Opp.of(Objects.requireNonNull(request).getMethod()).peek(exceptionLog::setRequestMethod);

        // 请求参数
        Opp<Opp<Object>> ofTry = Opp.ofTry(()
                -> Opp.of(joinPoint.getArgs()[0]).typeOfPeek((MultipartFile m)
                -> exceptionLog.setRequestParam(ResultConstant.FileMessage.FILE_EXCEPTION)));

        Opp.of(ofTry.getException()).ifPresent(k -> exceptionLog.setRequestParam("当前发送异常方法没有参数"));

        Opp.of(exceptionLog.getRequestParam()).orElseRun(()
                -> exceptionLog.setRequestParam(JSON.toJSONString(joinPoint.getArgs())));

        // 请求方法描述
        Opp.of(apiOperation).ifPresent(api -> exceptionLog.setOptDesc(api.value()));

        // 上边一系列处理
        // 下边判断是否符合然后发布事件
        // 事件发布
        applicationContext.publishEvent(new ExceptionLogEvent(exceptionLog));
    }


}
